<?php
/**
 * Created by PhpStorm
 * User: shen.zk
 * Date: 2023/9/13
 * Time: 15:40
 * Comment: ''
 */

namespace App\Service\Common;

use App\Service\BaseService;
use App\Repository\GoodsRepo;
use App\Params\Common\GoodsParams;
use App\Service\Common\MerchantsService;
use App\Common\ResponseCode;
use App\Exceptions\BusinessException;
use App\Repository\MemberRepo;
use Illuminate\Support\Facades\DB;
use App\Repository\GoodsLogRepo;
use App\Common\Tools;
use App\Repository\SpecialRepo;
use Illuminate\Support\Facades\Log;
use App\Params\Common\GoodsMoveParams;
use App\Extensions\Common\ChainService;
use App\Extensions\Common\RedisService;
use App\Common\Constant;
use App\Repository\ConfigRepo;
use App\Repository\SpecialPrivilegeRepo;
use App\Repository\SpecialPrivilegeOrderRepo;
use App\Params\Common\GoodsUgcBindParams;
use App\Repository\MerchantsInfoRepo;
use App\Extensions\Coupon\CouponService;
use App\Repository\SpecialPrivilegeUgcOrderRepo;
use App\Params\Common\GoodsUgcMoveParams;
use App\Extensions\Common\OssService;
use App\Jobs\UgcAutoReceiveJob;
use App\Params\Common\GoodsLockParams;
use App\Repository\OpenGoodsLockLogRepo;

class GoodsService extends BaseService
{

    /**
     * 商品详情
     * @param GoodsParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/9/20 15:01
     */
    public function detail(GoodsParams $params)
    {
        $access_mid = $params->getMid() ?: '';
        $mobile = $params->getMobile();
        $goods_id = $params->getGoodsId();

        $merchants_id = 0;
        if ($access_mid) {
            // 接入商户信息
            $merchants_info = MerchantsService::getInstance()->getInfoWithCache($access_mid);
            if (!$merchants_info) {
                throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
            }

            // 商户id
            $merchants_id = $merchants_info['merchants_id'];
        }

        // 用户信息
        $member = MemberRepo::getInstance()->getInfoByMobile($mobile);
        if (!$member) {
            throw new BusinessException(ResponseCode::USERNOTFUND);
        }

        // 用户id
        $member_id = $member['member_id'];

        // 商品详情
        $goods_info = GoodsRepo::getInstance()->getInfo($goods_id, $member_id, $merchants_id);
        if (!$goods_info) {
            throw new BusinessException(ResponseCode::GOODS_NOT_FUND);
        }

        $data = [
            'goods_id'     => $goods_info['goods_id'],
            'type'         => $goods_info['type'],
            'code'         => $goods_info['code'],
            'price'        => $goods_info['price'],
            'status'       => $goods_info['status'],
            'sell_status'  => $goods_info['sell_status'],
            'chain_mark'   => $goods_info['chain_mark'],
            'create_time'  => $goods_info['create_time'],
            'special_id'   => $goods_info['special_id'],
            'special_type' => $goods_info['special_type'],
            'title'        => $goods_info['title'],
            'thumb'        => $goods_info['thumb'],
            'cast_count'   => $goods_info['ca_cast_count'],
            'album_name'   => $goods_info['album_name'],
        ];

        return $data;
    }

    /**
     * 商品通兑
     * @param GoodsParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/9/20 16:37
     */
    public function exchange(GoodsParams $params)
    {
        $access_mid = $params->getMid();
        $mobile = $params->getMobile();
        $goods_id = $params->getGoodsId();

        // 频率限制
        $redis = RedisService::getInstance();
        $access_key = Constant::GOODS_EXCHANGE_ACCESS . $access_mid . '_' . $mobile . '_' . $goods_id;
        $redis->accessControl($access_key, 5);

        // 接入商户信息
        $merchants_info = MerchantsService::getInstance()->getInfoWithCache($access_mid);
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 商户id
        $merchants_id = $merchants_info['merchants_id'];

        // 用户信息
        $member = MemberRepo::getInstance()->getInfoByMobile($mobile);
        if (!$member) {
            throw new BusinessException(ResponseCode::USERNOTFUND);
        }

        // 用户id
        $member_id = $member['member_id'];

        // 商品详情
        $goods_info = GoodsRepo::getInstance()->getInfo($goods_id, $member_id, $merchants_id);
        if (!$goods_info || $goods_info['type'] != 1) {
            throw new BusinessException(ResponseCode::GOODS_NOT_FUND);
        }

        // 商品被锁定
        if ($goods_info['sell_status'] != 0) {
            throw new BusinessException(ResponseCode::GOODS_SELL_STATUS_LOCKED);
        }

        // 创作者的藏品详情
        $special_info = SpecialRepo::getInstance()->detail($goods_info['special_id'], $access_mid);
        if (!$special_info) {
            throw new BusinessException(ResponseCode::SPECIAL_NOT_FUND);
        }

        // 藏品未通过审核
//        if ($special_info['apply_status'] != 2) {
//            throw new BusinessException(ResponseCode::SPECIAL_APPLY_FAIL);
//        }

        // 藏品未铸造完成
        if ($special_info['cast_status'] != 3) {
            throw new BusinessException(ResponseCode::SPECIAL_CAST_NOT_FINISH);
        }

        try {
            DB::beginTransaction();

            // 1.更新商品信息
            $data1 = [
                'member_id'   => -1,
                'status'      => 8,
                'update_time' => date("Y-m-d H:i:s"),
            ];
            $res1 = GoodsRepo::getInstance()->save($data1, $goods_id);

            // 2.增加商品记录
            $data2 = [
                'goods_id'       => $goods_id,
                'special_id'     => $goods_info['special_id'],
                'from_member_id' => $member_id,
                'order_sn'       => Tools::build_hash_no(),
                'content'        => "开放平台通兑",
                'type'           => 10,
                'source'         => 3,
                'create_time'    => date('Y-m-d H:i:s')
            ];
            $res2 = GoodsLogRepo::getInstance()->save($data2);

            // 3.更新藏品表的通兑数量
            $res3 = SpecialRepo::getInstance()->updateExchangeNum($goods_info['special_id']);

            if (!$res1 || !$res2 || !$res3) {
                DB::rollBack();

                Log::info('[商品通兑异常]' . $goods_id . '|' . $res1 . '|' . $res2 . '|' . $res3);
                throw new BusinessException(ResponseCode::GOODS_EXCHANGE_ERROR);
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            Log::info('[商品通兑错误]' . $goods_id);
            throw new BusinessException(ResponseCode::GOODS_EXCHANGE_FAIL);
        }

        $data = [
            'goods_id' => $goods_id,
        ];

        $redis->del($access_key);
        return $data;
    }

    /**
     * 商品通兑状态
     * @param GoodsParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/10/7 9:58
     */
    public function exchangeStatus(GoodsParams $params)
    {
        $access_mid = $params->getMid();
        $mobile = $params->getMobile();
        $goods_id = $params->getGoodsId();

        // 接入商户信息
        $merchants_info = MerchantsService::getInstance()->getInfoWithCache($access_mid);
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 商户id
        $merchants_id = $merchants_info['merchants_id'];

        // 用户信息
        $member = MemberRepo::getInstance()->getInfoByMobile($mobile);
        if (!$member) {
            throw new BusinessException(ResponseCode::USERNOTFUND);
        }

        // 用户id
        $member_id = $member['member_id'];

        // 商品的通兑记录
        $log = GoodsLogRepo::getInstance()->detail($goods_id, $member_id, 0, 10);

        $data = [
            'goods_id'        => $goods_id,
            'exchange_status' => 0,
        ];
        if ($log) {
            $data['exchange_status'] = 1;
            $data['create_time'] = $log['create_time'];
        }
        return $data;
    }

    /**
     * 商品转移
     * @param GoodsMoveParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/9/20 18:32
     */
    public function move(GoodsMoveParams $params)
    {
        $platform_no = $params->getAccessMid();
        $mid = $params->getMid() ?: '';
        $from_mobile = $params->getFromMobile();
        $to_mobile = $params->getToMobile();
        $goods_id = $params->getGoodsId();
        $lock_serial_number = $params->getLockSerialNumber();

        // 频率限制
        $redis = RedisService::getInstance();
        $access_key = Constant::GOODS_MOVE_ACCESS . $mid . '_' . $from_mobile . '_' . $to_mobile . $goods_id;
        $redis->accessControl($access_key, 5);

        $merchants_id = 0;
        if ($mid) {
            // 接入商户信息
            $merchants_info = MerchantsService::getInstance()->getInfoWithCache($mid);
            if (!$merchants_info) {
                throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
            }

            // 商户id
            $merchants_id = $merchants_info['merchants_id'];
        }

        // 相同手机号
        if ($from_mobile == $to_mobile) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_USER_SAME);
        }

        // 转移用户信息
        $from_member = MemberRepo::getInstance()->getInfoByMobile($from_mobile);
        if (!$from_member) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_FROM_USER_NOT_FUND);
        }

        // 转移用户未实名
        if ($from_member['real_name_status'] != 2) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_FROM_USER_REAL_NAME_UNFINISHED);
        }

        // 接收用户信息
        $to_member = MemberRepo::getInstance()->getInfoByMobile($to_mobile);
        if (!$to_member) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_TO_USER_NOT_FUND);
        }

        //  接收用户未实名
        if ($to_member['real_name_status'] != 2) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_TO_USER_REAL_NAME_UNFINISHED);
        }

        // 转移用户id
        $from_member_id = $from_member['member_id'];

        // 接收用户id
        $to_member_id = $to_member['member_id'];

        // 转移商品详情
        $goods_info = GoodsRepo::getInstance()->getInfo($goods_id, $from_member_id, $merchants_id);
        if (!$goods_info || $goods_info['type'] != 1) {
            throw new BusinessException(ResponseCode::GOODS_NOT_FUND);
        }

        // 商品未被锁定(谁锁定,谁转移,谁解锁)
        if ($goods_info['sell_status'] != 1) {
            throw new BusinessException(ResponseCode::GOODS_SELL_STATUS_UNLOCKED);
        }

        $special_id = $goods_info['special_id'];

        // 商品转赠时间限制
        $this->checkTransferTime($goods_info);

        // 藏品赋能未全部领取
        $all_received = $this->checkPrivilegeAllReceived($special_id, $goods_id);
        if (!$all_received) {
            //throw new BusinessException(ResponseCode::SPECIAL_PRIVILEGE_NOT_ALL_RECEIVED);
        }

        // ugc赋能未全部领取
        $ugc_all_receives = $this->checkUgcAllReceived($goods_id);
        if (!$ugc_all_receives) {
            //throw new BusinessException(ResponseCode::GOODS_UGC_PRIVILEGE_NOT_ALL_RECEIVED);
        }

        // 平台
        $platform_info = PlatformService::getInstance()->getInfoWithCache($platform_no);
        $platform_id = $platform_info['id'];

        // 校验锁定商品的流水号(谁锁定,谁转移,谁解锁)
        $this->checkLockSerialNumber($lock_serial_number, $platform_id, $goods_id);

        try {
            DB::beginTransaction();

            // 1.更新商品信息
            $data1 = [
                'member_id'   => $to_member_id,
                'stand_time'  => date('Y-m-d H:i:s'),
                'update_time' => date("Y-m-d H:i:s"),
                'sell_status' => 0,// 解锁
            ];
            $res1 = GoodsRepo::getInstance()->save($data1, $goods_id);

            // 2.增加商品记录
            $data2 = [
                'goods_id'       => $goods_id,
                'special_id'     => $goods_info['special_id'],
                'from_member_id' => $from_member_id,
                'to_member_id'   => $to_member_id,
                'order_sn'       => Tools::build_hash_no(),
                'content'        => "开放平台转移",
                'type'           => 2,
                'source'         => 3,
                'create_time'    => date('Y-m-d H:i:s')
            ];
            $res2 = GoodsLogRepo::getInstance()->save($data2);

            // 3.增加商品解锁记录
            $data3 = [
                'serial_number'      => Tools::build_hash_no(),
                'type'               => 2,
                'lock_serial_number' => $lock_serial_number,
                'platform_id'        => $platform_id,
                'goods_id'           => $goods_id,
                'create_time'        => date('Y-m-d H:i:s')
            ];
            $res3 = OpenGoodsLockLogRepo::getInstance()->save($data3);

            if (!$res1 || !$res2 || !$res3) {
                Log::info('[商品转移异常1]' . $goods_id . '|' . $res1 . '|' . $res2 . '|' . $res3);
                throw new BusinessException(ResponseCode::GOODS_MOVE_ERROR);
            }

            // 4.nft转账
            $chain = ChainService::getInstance();
            $res4 = $chain->transferFrom($from_member['ca_account_address'], $to_member['ca_account_address'], $goods_info['chain_mark']);
            if (!$res4) {
                Log::info('[商品转移异常2]' . $goods_id . '|' . $res4);
                throw new BusinessException(ResponseCode::GOODS_MOVE_ERROR);
            }

            // 5.更新hash
            $res5 = GoodsLogRepo::getInstance()->save(['hash' => $res4], $res2);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            Log::info('[商品转移失败]' . $goods_id . '_' . $e->getFile() . '_' . $e->getLine() . '_' . $e->getMessage());
            throw new BusinessException(ResponseCode::GOODS_MOVE_FAIL, ['error' => $e->getMessage()]);
        }

        $data = [
            'goods_id' => $goods_id,
        ];

        $redis->del($access_key);
        return $data;
    }

    /**
     * 商品转赠时间限制
     * @param $goods_info
     * @return void
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/17 16:43
     */
    private function checkTransferTime($goods_info)
    {
        // 转赠时间限制
        $transfer_time = ConfigRepo::getInstance()->getConfigValue('transfer_time');
        $time = $goods_info['stand_time'] ?: $goods_info['create_time'];
        if ((strtotime($time) + $transfer_time * 60 * 60) > time()) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_TRANSFER_TIME_ERROR);
        }
    }

    /**
     * 校验藏品的赋能是否被全部领取
     * @param int $special_id
     * @param int $goods_id
     * @return bool
     * Author:xsc
     * Time:2023/10/8 11:11
     */
    public function checkPrivilegeAllReceived(int $special_id, int $goods_id)
    {
        // 藏品的赋能记录
        $privilege_list = SpecialPrivilegeRepo::getInstance()->list($special_id);

        if ($privilege_list) {
            $privilege_ids = array_column($privilege_list, 'id');

            // 赋能领取记录
            $order_list = SpecialPrivilegeOrderRepo::getInstance()->getListByPrivilegeIds($privilege_ids, $goods_id);

            if (count($privilege_list) != count($order_list)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 校验ugc赋能是否被全部领取
     * @param int $goods_id
     * @return bool
     * Author:xsc
     * Time:2023/10/16 16:50
     */
    public function checkUgcAllReceived(int $goods_id)
    {
        // 未结束的订单
        $order_list = SpecialPrivilegeUgcOrderRepo::getInstance()->getUnfinishedOrder($goods_id);
        if ($order_list) {
            return false;
        }
        return true;
    }

    /**
     * ugc赋能绑定
     * @param GoodsUgcBindParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/10/10 16:10
     */
    public function ugcBind(GoodsUgcBindParams $params)
    {
        $mobile = $params->getMobile();
        $goods_id = $params->getGoodsId();
        $third_no = $params->getThirdNo();
        $coupon = $params->getCoupon() ?: '';
        $envelope = $params->getEnvelope() ?: '';

        // 频率限制
        $redis = RedisService::getInstance();
        $access_key = Constant::GOODS_UGC_BIND_ACCESS . $third_no;
        $redis->accessControl($access_key, 5);

        // 用户信息
        $member = MemberRepo::getInstance()->getInfoByMobile($mobile);
        if (!$member) {
            throw new BusinessException(ResponseCode::USERNOTFUND);
        }

        // 用户id
        $member_id = $member['member_id'];

        // 商品详情
        $goods_info = GoodsRepo::getInstance()->getInfo($goods_id, $member_id);
        if (!$goods_info || $goods_info['channel'] != 2) {
            throw new BusinessException(ResponseCode::GOODS_NOT_FUND);
        }

        // 商品被锁定
        if ($goods_info['sell_status'] != 0) {
            throw new BusinessException(ResponseCode::GOODS_SELL_STATUS_LOCKED);
        }

        // 商品转赠时间限制
        $this->checkTransferTime($goods_info);

        // 藏品赋能未全部领取
        $all_received = $this->checkPrivilegeAllReceived($goods_info['special_id'], $goods_id);
        if (!$all_received) {
            throw new BusinessException(ResponseCode::SPECIAL_PRIVILEGE_NOT_ALL_RECEIVED);
        }

        // ugc赋能未全部领取
        $ugc_all_receives = $this->checkUgcAllReceived($goods_id);
        if (!$ugc_all_receives) {
            throw new BusinessException(ResponseCode::GOODS_UGC_PRIVILEGE_NOT_ALL_RECEIVED);
        }

        // 第三方订单号唯一
        $order_list = SpecialPrivilegeUgcOrderRepo::getInstance()->list(['thirdNo' => $third_no,]);
        if ($order_list) {
            throw new BusinessException(ResponseCode::GOODS_UGC_ORDER_EXISTS);
        }

        try {
            // 校验ugc赋能绑定的参数
            $list = $this->checkUgcBind($coupon, $envelope);
            foreach ($list as $value) {
                // 处理ugc赋能绑定
                $res = $this->dealUgcBind($third_no, $value, $goods_info);
                if (!$res) {
                    throw new BusinessException(ResponseCode::GOODS_UGC_BIND_ORDER_DEAL_ERROR);
                }
            }
        } catch (\Exception $e) {
            // 有一个失败,就把所有订单标记为失败,冻结的卡券全部解冻
            $this->unfreeze($third_no);
            Log::info('[ugc订单异常3]' . $third_no);
            throw new BusinessException(ResponseCode::GOODS_UGC_BIND_ORDER_DEAL_FAIL, ['error' => $e->getMessage()]);
        }

        // 全部成功之后锁定商品
        goodsRepo::getInstance()->save(['sell_status' => 1], $goods_id);

        $data = [
            'goods_id' => $goods_id,
        ];

        $redis->del($access_key);
        return $data;
    }

    /**
     * 校验ugc赋能绑定的参数
     * @param string $coupon
     * @param string $envelope
     * @return array
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/10 16:11
     */
    private function checkUgcBind(string $coupon = '', string $envelope = '')
    {
        $coupon = $coupon ? json_decode($coupon, true) : [];
        $envelope = $envelope ? json_decode($envelope, true) : [];

        if (!$coupon && !$envelope) {
            throw new BusinessException(ResponseCode::GOODS_UGC_BIND_PARAMS_ERROR);
        }

        $new_coupon = [];
        if ($coupon) {
            foreach ($coupon as $value) {
                if (!isset($value['sku_code']) || !isset($value['msg_code'])) {
                    throw new BusinessException(ResponseCode::GOODS_UGC_BIND_COUPON_PARAMS_ERROR);
                }
            }

            // 批量查询
            $coupon_service = CouponService::getInstance();
            $coupon_list = $coupon_service->batchCheckCard($coupon);

            foreach ($coupon_list as $k) {
                // 卡券状态
                if ($k['couponStatus'] != 0) {
                    throw new BusinessException(ResponseCode::COUPON_STATUS_ERROR, ['error' => $k['skuCode'] . '卡券状态异常']);
                }
                // 卡券过期
                if (strtotime($k['codeEndDate']) < time()) {
                    throw new BusinessException(ResponseCode::COUPON_EXPIRED, ['error' => $k['skuCode'] . '卡券已过期']);
                }
                if (strtotime($k['codeEndDate']) < (time() + 86400)) {
                    throw new BusinessException(ResponseCode::COUPON_EXPIRED, ['error' => $k['skuCode'] . '卡券24小时内过期']);
                }
                $new_coupon[] = [
                    'mark'     => 1,// 标识为卡券
                    'sku_code' => $k['skuCode'],
                    'msg_code' => $k['msgCode'],
                    'sku_name' => $k['skuName'],
                    'spu_name' => $k['spuName'],
                    'spu_code' => $k['spuCode'],
                    'end_date' => $k['codeEndDate'],
                    'thumb'    => isset($k['skuDetailPics'][0]['picUrl']) ? $k['skuDetailPics'][0]['picUrl'] : '',
                    'content'  => $k['skuDetail'] ?: '',
                    'notice'   => $k['spuDetail'] ?: '',
                ];
            }
            // 批量冻结
            $coupon_service->batchOperaCode($coupon);

/*            $coupon_service = CouponService::getInstance();
            foreach ($coupon as &$k) {
                if (!isset($k['sku_code']) || !isset($k['msg_code'])) {
                    throw new BusinessException(ResponseCode::GOODS_UGC_BIND_COUPON_PARAMS_ERROR);
                }

                // 查询卡券
                $sku_code = $k['sku_code'];
                $msg_code = $k['msg_code'];
                $coupon_info = $coupon_service->checkCard($sku_code, $msg_code);

                if (!$coupon_info) {
                    throw new BusinessException(ResponseCode::COUPON_NOT_FUND, ['error' => $sku_code . '卡券不存在']);
                }

                // 卡券状态
                if ($coupon_info['couponStatus'] != 0) {
                    throw new BusinessException(ResponseCode::COUPON_STATUS_ERROR, ['error' => $sku_code . '卡券状态异常']);
                }

                // 卡券过期
                if (strtotime($coupon_info['codeEndDate']) < time()) {
                    throw new BusinessException(ResponseCode::COUPON_EXPIRED, ['error' => $sku_code . '卡券已过期']);
                }

                if (strtotime($coupon_info['codeEndDate']) < (time() + 86400)) {
                    throw new BusinessException(ResponseCode::COUPON_EXPIRED, ['error' => $sku_code . '卡券24小时内过期']);
                }

                // 标识为卡券
                $k['mark'] = 1;
                $k['sku_name'] = $coupon_info['skuName'];
                $k['spu_name'] = $coupon_info['spuName'];
                $k['spu_code'] = $coupon_info['spuCode'];
                $k['end_date'] = $coupon_info['codeEndDate'];
                $k['thumb'] = isset($coupon_info['skuDetailPics'][0]['picUrl']) ? $coupon_info['skuDetailPics'][0]['picUrl'] : '';
                $k['content'] = $coupon_info['skuDetail'] ?: '';
                $k['notice'] = $coupon_info['spuDetail'] ?: '';
            }*/
        }

        $fields = ['sku_code', 'sku_name', 'type', 'stock', 'end_date', 'amount',];
        if ($envelope) {
            foreach ($envelope as &$v) {
                foreach ($fields as $field) {
                    if (!isset($v[$field])) {
                        throw new BusinessException(ResponseCode::GOODS_UGC_BIND_ENVELOPE_PARAMS_ERROR);
                    }
                }

                $sku_code = $v['sku_code'];
                // 库存
                if ($v['stock'] != 1) {
                    throw new BusinessException(ResponseCode::ENVELOPE_STOCK_ERROR, ['error' => $sku_code . '红包库存错误']);
                }

                // 限制红包有效期
                if (strtotime($v['end_date']) < strtotime(date('Ymd'))) {
                    throw new BusinessException(ResponseCode::ENVELOPE_END_DATE_ERROR, ['error' => $sku_code . '红包有效期错误']);
                }

                // 限制红包金额>0.01
                if ($v['amount'] < 0.01) {
                    throw new BusinessException(ResponseCode::ENVELOPE_AMOUNT_ERROR, ['error' => $sku_code . '红包金额错误']);
                }

                // 标识为红包
                $v['mark'] = 2;
            }
        }
        return array_merge($new_coupon, $envelope);
    }

    /**
     * 处理ugc赋能绑定
     * @param string $third_no
     * @param array $data
     * @param array $goods_info
     * @return bool
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/10 16:09
     */
    private function dealUgcBind(string $third_no, array $data, array $goods_info)
    {
        $sku_code = $data['sku_code'];

        if ($data['mark'] == 1) {
            // 卡券
            $title = $data['sku_name'];
            $intro = $data['spu_name'];
            $content = $data['content'];
            $notice = $data['notice'];
            $type = 1;
            $rp_type = 0;
            $rp_amount = null;
            $coupon_id = $data['spu_code'];
            $woff_code = $data['msg_code'];
            $end_time = $data['end_date'];
            $thumb = '';
            if ($data['thumb']) {
                $thumb = OssService::getInstance()->saveFile($data['thumb']);
            }
        } else if ($data['mark'] == 2) {
            // 红包

            // 读取配置中的红包信息配置
            $config_info = ConfigRepo::getInstance()->getConfigValue('envelope_' . $data['type']);
            $config_info = json_decode($config_info, true);
            $title = str_replace('{$amount}', $data['amount'], $config_info['title']);
            $intro = $config_info['desc'];
            $content = $config_info['content'];
            $notice = $config_info['notice'];
            $type = 2;
            $rp_type = intval($data['type']);
            $rp_amount = $data['amount'];
            $coupon_id = '';
            $woff_code = '';
            $end_time = date('Y-m-d 23:59:59', strtotime($data['end_date']));
            $thumb = $config_info['thumb'];
        }

        // 1.插入订单
        $order_sn = Tools::build_ugc_order_no();
        $data1 = [
            'ordersn'        => $order_sn,
            'title'          => $title,
            'thumb'          => $thumb,
            'intro'          => $intro,
            'content'        => $content,
            'notice'         => $notice,
            'status'         => 0,
            'special_id'     => $goods_info['special_id'],
            'goods_id'       => $goods_info['goods_id'],
            'code'           => $goods_info['code'],
            'type'           => $type,
            'rp_type'        => $rp_type,
            'rp_amount'      => $rp_amount,
            //'mer_id'         => $merchants_id,
            'from_member_id' => $goods_info['member_id'],
            'coupon_code'    => $sku_code,
            'coupon_id'      => $coupon_id,
            'woff_code'      => $woff_code,
            'thirdNo'        => $third_no,
            'end_time'       => $end_time,
            'create_time'    => date('Y-m-d H:i:s'),
        ];

        $res1 = SpecialPrivilegeUgcOrderRepo::getInstance()->save($data1);
        if (!$res1) {
            Log::info('[ugc订单异常1]' . $third_no . '|' . $sku_code . '|' . $res1);
            return false;
        }

        // 2.冻结卡券/创建红包
        $coupon_service = CouponService::getInstance();
        if ($type == 1) {
            // 冻结卡券
            //$res2 = $coupon_service->operaCode($sku_code, $data['msg_code'], 0);
            $res2 = true;// 在checkUgcBind中已经批量冻结过了
        } else if ($type == 2) {
            // 创建红包,用汇筑的账号
            $res2 = $coupon_service->createRedEnvelope(config('coupon.channel_code'), $sku_code, $data['sku_name'], $data['type'], $data['stock'], $data['end_date'], $data['amount']);
        }
        if (!$res2) {
            SpecialPrivilegeUgcOrderRepo::getInstance()->save(['status' => 6], $res1);
            Log::info('[ugc订单异常2]' . $third_no . '|' . $sku_code . '|' . $res2);
            return false;
        }

        return true;
    }

    /**
     * 全部解冻
     * @param string $third_no
     * @return void
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/10 16:07
     */
    private function unfreeze(string $third_no)
    {
        $where = [
            'status'  => 0,
            'thirdNo' => $third_no,
        ];
        $order_list = SpecialPrivilegeUgcOrderRepo::getInstance()->list($where);
        if (!$order_list) {
            return;
        }

        $coupon_service = CouponService::getInstance();
        foreach ($order_list as $value) {
            if ($value['type'] == 1) {
                // 解冻之前,把单个订单标记为撤销中
                SpecialPrivilegeUgcOrderRepo::getInstance()->save(['status' => 4], $value['id']);

                // 解冻
                $res = $coupon_service->operaCode($value['coupon_code'], $value['woff_code'], 1);
                if ($res) {
                    SpecialPrivilegeUgcOrderRepo::getInstance()->save(['status' => 6], $value['id']);
                }

            } else if ($value['type'] == 2) {
                SpecialPrivilegeUgcOrderRepo::getInstance()->save(['status' => 6], $value['id']);
            }
        }
    }

    /**
     * ugc赋能解绑
     * @param GoodsUgcBindParams $params
     * @return array
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/10 17:20
     */
    public function ugcUnbind(GoodsUgcBindParams $params)
    {
        $mobile = $params->getMobile();
        $goods_id = $params->getGoodsId();
        $third_no = $params->getThirdNo();

        // 用户信息
        $member = MemberRepo::getInstance()->getInfoByMobile($mobile);
        if (!$member) {
            throw new BusinessException(ResponseCode::USERNOTFUND);
        }

        // 用户id
        $member_id = $member['member_id'];

        // 商品详情
        $goods_info = GoodsRepo::getInstance()->getInfo($goods_id, $member_id);
        if (!$goods_info) {
            throw new BusinessException(ResponseCode::GOODS_NOT_FUND);
        }

        $where = [
            'status'         => 0,
            'goods_id'       => $goods_id,
            'from_member_id' => $member_id,
            'thirdNo'        => $third_no,
        ];
        $order_list = SpecialPrivilegeUgcOrderRepo::getInstance()->list($where);
        if (!$order_list) {
            throw new BusinessException(ResponseCode::GOODS_UGC_ORDER_NOT_EXISTS, ['error' => $third_no . '无可解绑的赋能']);
        }

        $error = '';
        $coupon_service = CouponService::getInstance();
        foreach ($order_list as $value) {

            $result = false;
            if ($value['type'] == 1) {
                $sku_code = $value['coupon_code'];
                $msg_code = $value['woff_code'];

                //解冻之前,把单个订单标记为撤销中
                SpecialPrivilegeUgcOrderRepo::getInstance()->save(['status' => 4], $value['id']);

                // 卡券解冻
                $res = $coupon_service->operaCode($sku_code, $msg_code, 1);
                if ($res) {
                    $result = SpecialPrivilegeUgcOrderRepo::getInstance()->save(['status' => 5], $value['id']);
                } else {
                    SpecialPrivilegeUgcOrderRepo::getInstance()->save(['status' => 0], $value['id']);
                }
            } else if ($value['type'] == 2) {
                $result = SpecialPrivilegeUgcOrderRepo::getInstance()->save(['status' => 5], $value['id']);
            }

            if (!$result) {
                $error .= $sku_code . '解绑失败,';
            }
        }

        if (!$error) {
            // 全部解绑之后解锁商品
            goodsRepo::getInstance()->save(['sell_status' => 0], $goods_id);
        }

        if ($error) {
            throw new BusinessException(ResponseCode::GOODS_UGC_UNBIND_FAIL, ['error' => trim($error, ',')]);
        }

        $data = [
            'goods_id' => $goods_id,
        ];

        return $data;
    }

    /**
     * ugc商品转移
     * @param GoodsUgcMoveParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/10/16 9:43
     */
    public function ugcMove(GoodsUgcMoveParams $params)
    {
        $third_no = $params->getThirdNo();
        $to_mobile = $params->getToMobile();

        // 频率限制
        $redis = RedisService::getInstance();
        $access_key = Constant::GOODS_UGC_MOVE . $third_no;
        $redis->accessControl($access_key, 5);

        // 接收用户信息
        $to_member = MemberRepo::getInstance()->getInfoByMobile($to_mobile);
        if (!$to_member) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_TO_USER_NOT_FUND);
        }

        // 接收用户未实名
        if ($to_member['real_name_status'] != 2) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_TO_USER_REAL_NAME_UNFINISHED);
        }

        // 判断是否有ca_account_address
        if (empty($to_member['ca_account_address'])) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_TO_USER_AMOUNT_ERROR);
        }

        // 接收用户id
        $to_member_id = $to_member['member_id'];

        // 订单
        $where = [
            'thirdNo' => $third_no,
        ];
        $order_list = SpecialPrivilegeUgcOrderRepo::getInstance()->list($where);
        if (!$order_list) {
            throw new BusinessException(ResponseCode::GOODS_UGC_ORDER_NOT_EXISTS);
        }

        // 转移用户id
        $from_member_id = $order_list[0]['from_member_id'];

        // 转移用户信息
        $from_member = MemberRepo::getInstance()->getInfoById($from_member_id);

        if (!$from_member) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_FROM_USER_NOT_FUND);
        }

        // 相同用户
        if ($from_member_id == $to_member_id) {
            throw new BusinessException(ResponseCode::GOODS_MOVE_USER_SAME);
        }

        // 商品详情
        $goods_id = $order_list[0]['goods_id'];
        $goods_info = GoodsRepo::getInstance()->getInfo($goods_id, $from_member_id);
        if (!$goods_info || $goods_info['type'] != 1) {
            throw new BusinessException(ResponseCode::GOODS_NOT_FUND);
        }

        // 商品未被锁定
        if ($goods_info['sell_status'] != 1) {
            throw new BusinessException(ResponseCode::GOODS_SELL_STATUS_UNLOCKED);
        }

        // 商品转赠时间限制
        $this->checkTransferTime($goods_info);

        // 藏品赋能未全部领取
        $all_received = $this->checkPrivilegeAllReceived($goods_info['special_id'], $goods_id);
        if (!$all_received) {
            throw new BusinessException(ResponseCode::SPECIAL_PRIVILEGE_NOT_ALL_RECEIVED);
        }

        // 所有订单的status必须都是0
        foreach ($order_list as $value) {
            if ($value['status'] != 0) {
                throw new BusinessException(ResponseCode::GOODS_UGC_ORDER_STATUS_ERROR);
            }
        }

        try {
            DB::beginTransaction();

            // 1.更新商品信息
            $data1 = [
                'member_id'   => $to_member_id,
                'stand_time'  => date('Y-m-d H:i:s'),
                'update_time' => date("Y-m-d H:i:s"),
            ];
            $res1 = GoodsRepo::getInstance()->save($data1, $goods_id);

            // 2.增加商品记录
            $data2 = [
                'goods_id'       => $goods_id,
                'special_id'     => $goods_info['special_id'],
                'from_member_id' => $from_member_id,
                'to_member_id'   => $to_member_id,
                'order_sn'       => Tools::build_hash_no(),
                'content'        => "开放平台ugc转移",
                'type'           => 2,
                'source'         => 4,
                'create_time'    => date('Y-m-d H:i:s')
            ];
            $res2 = GoodsLogRepo::getInstance()->save($data2);

            if (!$res1 || !$res2) {
                Log::info('[商品ugc转移异常]' . $goods_id . '|' . $res1 . '|' . $res2);
                throw new BusinessException(ResponseCode::GOODS_MOVE_ERROR);
            }

            // 3.nft转账
            $chain = ChainService::getInstance();
            $res3 = $chain->transferFrom($from_member['ca_account_address'], $to_member['ca_account_address'], $goods_info['chain_mark']);

            if (!$res3) {
                Log::info('[商品ugc转移异常],nft转账失败' . $goods_id . '|' . $res3);
                throw new BusinessException(ResponseCode::GOODS_MOVE_ERROR);
            }

            // 4.更新hash
            $res4 = GoodsLogRepo::getInstance()->save(['hash' => $res3], $res2);

            // 5.ugc订单更新为1未领取
            $res5 = SpecialPrivilegeUgcOrderRepo::getInstance()->updateByThirdNo(['to_member_id' => $to_member_id, 'status' => 1], $third_no);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            Log::info('[商品ugc转移失败]' . $goods_id);
            throw new BusinessException(ResponseCode::GOODS_MOVE_FAIL);
        }

        // 转移之后解锁商品
        goodsRepo::getInstance()->save(['sell_status' => 0], $goods_id);

        // 自动领取,异步
        foreach ($order_list as $value) {
            dispatch(new UgcAutoReceiveJob($value['id']));
        }

        $data = [
            'goods_id' => $goods_id,
        ];

        $redis->del($access_key);
        return $data;
    }

    /**
     * 商品锁定/解锁
     * @param GoodsLockParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/12/6 15:23
     */
    public function lock(GoodsLockParams $params)
    {
        $platform_no = $params->getAccessMid();
        $mid = $params->getMid() ?: '';
        $mobile = $params->getMobile();
        $goods_id = $params->getGoodsId();
        $lock_serial_number = $params->getLockSerialNumber();

        $merchants_id = 0;
        if ($mid) {
            // 接入商户信息
            $merchants_info = MerchantsService::getInstance()->getInfoWithCache($mid);
            if (!$merchants_info) {
                throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
            }

            // 商户id
            $merchants_id = $merchants_info['merchants_id'];
        }

        // 用户信息
        $member = MemberRepo::getInstance()->getInfoByMobile($mobile);
        if (!$member) {
            throw new BusinessException(ResponseCode::USERNOTFUND);
        }

        // 用户id
        $member_id = $member['member_id'];

        // 商品详情
        $goods_info = GoodsRepo::getInstance()->getInfo($goods_id, $member_id, $merchants_id);
        if (!$goods_info) {
            throw new BusinessException(ResponseCode::GOODS_NOT_FUND);
        }

        // 平台
        $platform_info = PlatformService::getInstance()->getInfoWithCache($platform_no);
        $platform_id = $platform_info['id'];

        if (!$lock_serial_number) {
            // 锁定
            if ($goods_info['sell_status'] != 0) {
                throw new BusinessException(ResponseCode::GOODS_SELL_STATUS_LOCKED, ['error' => '商品已被锁定,无法重复锁定']);
            }

            $type = 1;
            $sell_status = 1;
        } else {
            // 解锁
            if ($goods_info['sell_status'] != 1) {
                throw new BusinessException(ResponseCode::GOODS_SELL_STATUS_UNLOCKED, ['error' => '商品未被锁定,无法解锁']);
            }

            // 校验锁定商品的流水号
            $this->checkLockSerialNumber($lock_serial_number, $platform_id, $goods_id);

            $type = 2;
            $sell_status = 0;
        }

        try {
            DB::beginTransaction();

            // 1.更新商品锁定状态
            $res1 = GoodsRepo::getInstance()->save(['sell_status' => $sell_status], $goods_id);

            // 2.增加商品锁定/解锁记录
            $serial_number = Tools::build_hash_no();
            $data2 = [
                'serial_number'      => $serial_number,
                'type'               => $type,
                'lock_serial_number' => $lock_serial_number ?: '',
                'platform_id'        => $platform_id,
                'goods_id'           => $goods_id,
                'create_time'        => date('Y-m-d H:i:s')
            ];
            $res2 = OpenGoodsLockLogRepo::getInstance()->save($data2);

            if (!$res1 || !$res2) {
                Log::info('[商品锁定失败]' . $goods_id . '_' . $type . '_' . $res1 . '_' . $res2);
                throw new BusinessException(ResponseCode::GOODS_LOCK_ERROR);
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            throw new BusinessException(ResponseCode::GOODS_LOCK_ERROR, ['error' => $e->getMessage()]);
        }

        $data = [
            'serial_number' => $serial_number,
        ];

        return $data;
    }

    /**
     * 校验锁定商品的流水号
     * @param string $lock_serial_number
     * @param string $platform_id
     * @param int $goods_id
     * @return void
     * @throws BusinessException
     * Author:xsc
     * Time:2023/12/7 10:22
     */
    private function checkLockSerialNumber(string $lock_serial_number, string $platform_id, int $goods_id)
    {
        // 流水号是否存在
        $where1 = ['serial_number' => $lock_serial_number, 'platform_id' => $platform_id, 'goods_id' => $goods_id];
        $log1 = OpenGoodsLockLogRepo::getInstance()->getData($where1);
        if (!$log1) {
            throw new BusinessException(ResponseCode::GOODS_LOCK_SERIAL_NUMBER_NOT_EXISTS);
        }

        // 流水号是否已被使用
        $where2 = ['lock_serial_number' => $lock_serial_number, 'platform_id' => $platform_id, 'goods_id' => $goods_id];
        $log2 = OpenGoodsLockLogRepo::getInstance()->getData($where2);
        if ($log2) {
            throw new BusinessException(ResponseCode::GOODS_LOCK_SERIAL_NUMBER_USED);
        }
    }

}
